<template>
  <div class="codeMirror">
    <button @click="btnClick">点击</button>
    <el-input type="text" v-model="pageValue" />
    <el-tree :data="variableList" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
    <el-select v-model="select" @change="selectChange">
      <el-option v-for="item in SQLMAP" :key="item.label" :value="item.value" :label="item.label"></el-option>
    </el-select>
    <codemirror ref="codemirrorRef" v-model="codemirrorValue" :options="options"></codemirror>
  </div>
</template>

<script>
import { codemirror } from "vue-codemirror";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/idea.css";
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/addon/hint/show-hint';
import { parse, show, cstVisitor } from "sql-parser-cst";
const sql = `select * from claim_case_summary where aaa in ('ZR01','ZR02') and bbb not in ('ZR01','ZR02') and ccc = 1 and ddd != 1`
const parserSql = (sql, range) => {
  let cst
  try {
    cst = parse(sql, {
      dialect: "sqlite",
      // These are optional:
      includeSpaces: true, // Adds spaces/tabs
      includeNewlines: true, // Adds newlines
      includeComments: true, // Adds comments
      includeRange: true, // Adds source code location data
    });
  } catch (error) {
    console.log('数据 error', error)
    return undefined
  }
  if (!cst) return undefined
  let sqlNode
  try {
    const toUpper = cstVisitor({
      binary_expr: (be) => {
        if (be.left.type === 'identifier') {
          if (be.operator === '=' || be.operator === '!=') {
            const [begin, end] = be.range
            if (Number(range) >= begin && Number(range) <= end) {
              sqlNode = be
            }
          }
          if (be.operator.name && be.operator.name === 'IN') {
            const [begin, end] = be.range
            if (Number(range) >= begin && Number(range) <= end) {
              sqlNode = be
            }
          }
          if (Array.isArray(be.operator) && be.operator.length === 2 && be.operator[0].name === 'NOT' && be.operator[1].name === 'IN') {
            const [begin, end] = be.range
            if (Number(range) >= begin && Number(range) <= end) {
              sqlNode = be
            }
          }
        }
      }
    });
    toUpper(cst);
  } catch (error) {
    console.log('数据 error', error)
    return undefined
  }
  return sqlNode
}
parserSql(sql, 60)
const findNode = (sqlValue, range) => {
  const before = `select * from claim_case_summary where `
  const sql = `${before}${sqlValue}`
  const lineRange = Number(range) + before.length
  return parserSql(sql, lineRange)
}


const variableList = [
  {
    label: '险类',
    value: 'risk_code',
    children: [
      {
        label: 'ZR01',
        value: "'ZR01'"
      },
      {
        label: 'ZR02',
        value: "'ZR02'"
      }
    ]
  },
  {
    label: '业务归属事业部',
    value: 'company_code',
    children: [
      {
        label: '健康险事业部',
        value: "('01','02','03')"
      }
    ]
  }
]

const SQLMAP = [
  {
    label: '如果',
    value: 'IF'
  },
  {
    label: '在',
    value: 'IN',
    tip: true,
    list: [
      {
        label: '()',
      }
    ]
  },
  {
    label: '不在',
    value: 'NOT IN',
    tip: true,
    list: [
      {
        label: '()',
      }
    ]
  },
  {
    label: '并且',
    value: 'AND'
  },
  {
    label: '或者',
    value: 'OR'
  },
  {
    label: '等于',
    tip: true,
    value: '='
  },
  {
    label: '不等于',
    tip: true,
    value: '!='
  }]

export default {
  components: { codemirror },
  props: {
    readOnly: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      defaultProps: {},
      variableList,
      SQLMAP,
      select: '',
      pageValue: '111',
      codemirrorValue: '',
      options: {
        // 语言及语法模式
        mode: 'text/javascript',
        // 主题
        theme: "idea",
        // 显示函数
        line: true,
        lineNumbers: false,
        // 软换行
        lineWrapping: true,
        // tab宽度
        tabSize: 4,
        readOnly: this.readOnly,
      }
    }
  },
  watch: {
    // codemirrorValue() {
    //   this.matchText()
    // }
  },
  mounted() {
    this.$refs.codemirrorRef.codemirror.execCommand('goDocEnd')
    // setInterval(() => {
    //   console.log('文本内容', this.getValue())
    //   console.log('位置', this.$refs.codemirrorRef.codemirror.getCursor().ch)

    // }, 5000);
    // 代码提示功能 当用户有输入时，显示提示信息
    // this.$refs.codemirrorRef.codemirror.on('inputRead', () => {
    //   const cursor = this.getCursor()
    //   const node = findNode(this.getValue(), cursor.ch)
    //   if (!node) return
    //   const findMatch = variableList.find(item => item.value === node.left.name)
    //   if (!findMatch) return
    //   const children = findMatch.children.filter(item => item.label.includes(node.right.text))
    //   this.$refs.codemirrorRef.codemirror.showHint({
    //     completeSingle: false,
    //     hint: (cmInstance) => this.handleShowHint2(cmInstance, children, { line: cursor.line, ch: Number(cursor.ch) - node.right.text.length })
    //   });
    // })
    // this.foo()

  },
  methods: {
    foo() {
      const arr = [
        {
          "type": "text",
          "value": "",
          "label": ""
        },
        {
          "value": "IF",
          "label": "如果",
          "type": "symbol"
        },
        {
          "type": "text",
          "value": "",
          "label": ""
        },
        {
          "value": "risk_code",
          "label": "险类",
          "type": "variable"
        },
        {
          "type": "text",
          "value": "",
          "label": ""
        },
        {
          "value": "IN",
          "label": "在",
          "type": "symbol"
        },
        {
          "type": "text",
          "value": "(",
          "label": "("
        },
        {
          "value": "'ZR01'",
          "label": "ZR01",
          "type": "variable"
        },
        {
          "type": "text",
          "value": ",",
          "label": ","
        },
        {
          "value": "'ZR02'",
          "label": "ZR02",
          "type": "variable"
        },
        {
          "type": "text",
          "value": ")",
          "label": ")"
        }
      ]
      arr.forEach(item => {
        const codemirror = this.$refs.codemirrorRef.codemirror
        const form = this.getCursor()
        if (item.type === 'text') {
          codemirror.replaceRange(item.label, form)
        } else {
          this.markText(item, item.type)
        }
      })
    },
    matchText(form) {
      const line = this.getLineBefore(form)
      const arr = line.split(' ').filter(Boolean)
      const text = arr[arr.length - 1]
      return variableList.find(item => item.value === text)
    },
    selectChange(value) {
      const form = this.$refs.codemirrorRef.codemirror.getCursor()
      const node = this.SQLMAP.find(item => item.value === value)
      this.markText(node, 'symbol')
      if (node.list) {
        node.list.forEach(item => {
          this.$refs.codemirrorRef.codemirror.replaceSelection(item.label)
        })
        this.setCh(-1)
      }
      // if (node.tip) {
      //   const findMatch = this.matchText(form)
      //   if (!findMatch) return
      //   this.findMatch = findMatch
      //   this.$refs.codemirrorRef.codemirror.showHint({
      //     completeSingle: false,
      //     hint: (cmInstance) => this.handleShowHint2(cmInstance, this.findMatch.children)
      //   });
      // } else {
      //   this.findMatch = null
      // }

    },
    handleNodeClick(data, node) {
      console.log('数据 data', data)
      // if (!data.children) {
      //   const doc = this.$refs.codemirrorRef.codemirror.getDoc()
      //   console.log('数据 doc', doc)
      //   doc.undo()
      // }
      this.markText(data, 'variable')

    },
    handleShowHint2(cmInstance, ctx, renderTo) {
      let cursor = cmInstance.getCursor();
      const list = ctx.map(item => ({
        text: item.label,
        displayText: item.label,
        displayInfo: '',
        render: this.hintRender,
        hint: () => {
          this.markText(item, 'variable', renderTo)
          // if (ctx.length > 1) {
          //   this.$refs.codemirrorRef.codemirror.replaceSelection(',')
          //   this.$refs.codemirrorRef.codemirror.showHint({
          //     completeSingle: false,
          //     hint: (cmInstance) => this.handleShowHint2(cmInstance, ctx)
          //   });
          // }
        },
      }))
      return {
        list,
        from: cursor
      }
    },
    hintRender(element, self, data) {
      let div = document.createElement("div");
      div.setAttribute("class", "autocomplete-div");

      let divText = document.createElement("div");
      divText.setAttribute("class", "autocomplete-name");
      divText.innerText = data.displayText;

      let divInfo = document.createElement("div");
      divInfo.setAttribute("class", "autocomplete-hint");
      divInfo.innerText = data.displayInfo;

      div.appendChild(divText);
      div.appendChild(divInfo);
      element.appendChild(div);
    },
    btnClick() {
      // this.markText('aaaa', 'variable')
      const marks = this.$refs.codemirrorRef.codemirror.getAllMarks()
      console.log('数据 value', marks)
      console.log('数据 value', this.getValue())
      const value = this.getValue()
      const list = value.split(" ")
      const info = list.map(item => {
        const node = marks.find(itemx => itemx.attributes.label === item)
        console.log('数据 node', node)
        if (node) {
          return {
            value: node.attributes.value,
            label: node.attributes.label,
            type: node.attributes.type

          }
        } else {
          return {
            type: 'text',
            value: item,
            label: item
          }
        }
      })
      console.log('数据 info.join(" ")', info)
    },
    getCursor() {
      return this.$refs.codemirrorRef.codemirror.getCursor()
    },
    getLineBefore(position = this.getCursor()) {
      const { ch, line } = position
      return this.$refs.codemirrorRef.codemirror.getRange({ line, ch: 0 }, { line, ch })
    },
    setCh(ch = 0) {
      const position = this.getCursor()
      return this.$refs.codemirrorRef.codemirror.setCursor({ line: position.line, ch: Number(position.ch) + ch })
    },
    getValue() {
      return this.$refs.codemirrorRef.codemirror.getValue()
    },
    markText(node, type, form = this.getCursor()) {
      this.$refs.codemirrorRef.codemirror.replaceRange(` ${node.label} `, form)
      const to = this.$refs.codemirrorRef.codemirror.getCursor()
      const el = document.createElement('span')
      el.innerHTML = `${node.label}`
      el.className = type
      this.$refs.codemirrorRef.codemirror.markText(form, to, {
        atomic: true,
        replacedWith: el,
        // className: type,
        attributes: {
          ...node,
          type
        }
      })
    }
  }
}
</script>

<style lang="css" scoped>
.codeMirror {
  width: 100%;
  height: 200px;
}
</style>
<style>
.symbol {
  font-weight: 700;
  color: #ae4597;
  line-height: 14px;
  margin: 0 1px;
  padding: 0 2px;
}

.variable {
  background: #007bff;
  padding: 3px 5px;
  border-radius: 3px;
  color: #fff;
  margin: 0 1px;
}

body {
  background-color: pink;
}
</style>

<style>
.autocomplete-div {
  display: inline-block;
  width: 100%;
}

.autocomplete-name {
  display: inline-block;
}

.autocomplete-hint {
  display: inline-block;
  float: right;
  color: #0088ff;
  margin-left: 1em;
}

.aaaa {
  font-size: 20px;
}
</style>